Electron 生产环境代码加固方案
🎯 开篇核心考点提示
面试官常问:
- Electron 应用如何防止源码被反编译?
- asar 打包是否足够安全?如何增强?
- 如何在运行时保护敏感数据和核心逻辑?
- 企业级 Electron 应用的完整安全策略是什么?
核心原则:
⚠️ Electron 无法做到绝对安全,只能提高破解成本
本质:Node.js + Chromium → 前端代码天然是明文
📊 一、加固策略总览(四层防护)
| 层级 | 防护措施 | 难度提升 | 实现复杂度 |
|---|---|---|---|
| 源码层 | JS 混淆、字符串加密 | ⭐⭐⭐ | 低 |
| 构建层 | asar 打包、二次加密 | ⭐⭐⭐⭐ | 中 |
| 运行时 | 内存解密、防调试、完整性校验 | ⭐⭐⭐⭐⭐ | 高 |
| 分发层 | 代码签名、HTTPS 更新 | ⭐⭐⭐ | 中 |
完整防护流程图
mermaid
graph TD
A[开发阶段] --> B[JS 代码混淆]
B --> C[敏感字符串 AES 加密]
C --> D[构建打包]
D --> E[asar 打包]
E --> F[asar 二次加密]
F --> G[计算 Hash 校验值]
G --> H[运行时加载]
H --> I[内存解密 asar]
I --> J[防调试检测]
J --> K[Hash 完整性校验]
K --> L{校验通过?}
L -->|是| M[正常运行]
L -->|否| N[强制退出]
M --> O[代码签名验证]
O --> P[HTTPS 自动更新]🔐 二、源码层加固(基础必做)
2.1 JS 代码混淆
工具对比
| 工具 | 混淆强度 | 性能影响 | 适用场景 | 推荐指数 |
|---|---|---|---|---|
| javascript-obfuscator | ⭐⭐⭐⭐⭐ | 中等 | 生产环境 | ⭐⭐⭐⭐⭐ |
| terser | ⭐⭐ | 极低 | 仅压缩 | ⭐⭐⭐ |
| uglify-js | ⭐⭐ | 低 | 已淘汰 | ⭐ |
安装与配置
bash
npm install javascript-obfuscator --save-dev混淆脚本示例
js
// scripts/obfuscate.js
const JavaScriptObfuscator = require('javascript-obfuscator');
const fs = require('fs');
const path = require('path');
function obfuscateFile(inputPath, outputPath) {
const code = fs.readFileSync(inputPath, 'utf-8');
const result = JavaScriptObfuscator.obfuscate(code, {
// 核心混淆选项
compact: true, // 压缩代码
controlFlowFlattening: true, // 控制流扁平化(关键)
controlFlowFlatteningThreshold: 0.75, // 控制流打乱程度 0-1
deadCodeInjection: true, // 注入死代码
deadCodeInjectionThreshold: 0.4, // 死代码比例
stringArray: true, // 字符串数组化
stringArrayEncoding: ['base64'], // 字符串编码方式
stringArrayThreshold: 0.75, // 字符串加密比例
rotateStringArray: true, // 随机旋转字符串数组
shuffleStringArray: true, // 打乱字符串数组顺序
splitStrings: true, // 分割长字符串
numbersToExpressions: true, // 数字转表达式
simplify: true, // 简化代码
});
fs.writeFileSync(outputPath, result.getObfuscatedCode());
console.log(`✅ 混淆完成: ${outputPath}`);
}
// 批量处理 dist 目录下的所有 JS 文件
const distDir = path.join(__dirname, '../dist');
const files = fs.readdirSync(distDir).filter(f => f.endsWith('.js'));
files.forEach(file => {
const inputPath = path.join(distDir, file);
const outputPath = path.join(distDir, file.replace('.js', '.obf.js'));
obfuscateFile(inputPath, outputPath);
});关键参数解析
| 参数 | 作用 | 推荐值 | 注意事项 |
|---|---|---|---|
controlFlowFlattening | 打乱代码执行流程,增加逆向难度 | true | 会略微降低运行性能 |
deadCodeInjection | 插入无用的垃圾代码干扰分析 | true | 会增加代码体积 20-40% |
stringArrayEncoding | 对字符串进行编码加密 | ['base64'] | 可选 rc4 更强但更慢 |
stringArrayThreshold | 字符串加密比例 | 0.75 | 1.0 表示全部加密 |
2.2 核心逻辑后移(架构层面)
❌ 错误做法:前端硬编码
js
// renderer/index.js - 极易被破解
const LICENSE_KEY = 'ABC-123-XYZ';
if (inputLicense === LICENSE_KEY) {
unlockFeature();
}✅ 正确做法:后端验证
mermaid
sequenceDiagram
participant C as Electron 客户端
participant S as 后端 API
participant D as 数据库
C->>S: POST /api/verify-license
Note over C,S: 发送机器指纹 + License
S->>D: 查询 License 有效性
D-->>S: 返回验证结果
S-->>C: { valid: true, token: "xxx" }
C->>C: 本地缓存 Token
Note over C: 定期心跳验证js
// main/license-manager.js
const axios = require('axios');
const crypto = require('crypto');
class LicenseManager {
constructor() {
this.apiBase = 'https://api.yourapp.com';
this.token = null;
}
// 生成机器指纹
getMachineFingerprint() {
const mac = require('os').networkInterfaces().eth0?.[0]?.mac || 'unknown';
const hostname = require('os').hostname();
return crypto.createHash('sha256').update(`${mac}-${hostname}`).digest('hex');
}
// 验证 License
async verifyLicense(licenseKey) {
try {
const response = await axios.post(`${this.apiBase}/api/verify-license`, {
license: licenseKey,
fingerprint: this.getMachineFingerprint(),
timestamp: Date.now()
});
if (response.data.valid) {
this.token = response.data.token;
// 定时心跳验证
setInterval(() => this.heartbeat(), 5 * 60 * 1000);
return true;
}
return false;
} catch (error) {
console.error('License 验证失败:', error);
return false;
}
}
// 心跳检测
async heartbeat() {
if (!this.token) return;
try {
await axios.post(`${this.apiBase}/api/heartbeat`, {
token: this.token,
fingerprint: this.getMachineFingerprint()
});
} catch (error) {
// 连续失败则退出
this.failCount = (this.failCount || 0) + 1;
if (this.failCount > 3) {
app.quit();
}
}
}
}
module.exports = new LicenseManager();2.3 敏感字符串加密
AES-256-CBC 加密工具
js
// utils/crypto-helper.js
const crypto = require('crypto');
const ALGORITHM = 'aes-256-cbc';
const KEY_LENGTH = 32; // 256 bits
const IV_LENGTH = 16; // 128 bits
class CryptoHelper {
constructor(secretKey) {
// 确保密钥长度为 32 字节
this.key = Buffer.from(secretKey.padEnd(KEY_LENGTH, '0').slice(0, KEY_LENGTH));
}
// 加密
encrypt(text) {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, this.key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
// IV + 密文
return iv.toString('hex') + ':' + encrypted;
}
// 解密
decrypt(encryptedText) {
const parts = encryptedText.split(':');
const iv = Buffer.from(parts[0], 'hex');
const encrypted = parts[1];
const decipher = crypto.createDecipheriv(ALGORITHM, this.key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// 使用示例
const helper = new CryptoHelper(process.env.ENCRYPTION_KEY);
// 编译时加密敏感信息
const ENCRYPTED_API_KEY = helper.encrypt('sk_live_abc123xyz');
console.log('加密后:', ENCRYPTED_API_KEY);
// 运行时解密
const apiKey = helper.decrypt(ENCRYPTED_API_KEY);预加密脚本
js
// scripts/encrypt-config.js
const CryptoHelper = require('../utils/crypto-helper');
const fs = require('fs');
const helper = new CryptoHelper(process.argv[2]); // 从命令行传入密钥
const config = {
apiUrl: 'https://api.example.com',
apiKey: 'secret-key-123',
databaseUrl: 'mongodb://user:pass@localhost'
};
const encrypted = {};
for (const [key, value] of Object.entries(config)) {
encrypted[key] = helper.encrypt(value);
}
fs.writeFileSync('config.encrypted.json', JSON.stringify(encrypted, null, 2));
console.log('✅ 配置加密完成');📦 三、构建层加固(Electron 核心)
3.1 asar 打包基础
electron-builder 配置
json
// package.json
{
"build": {
"appId": "com.yourapp.electron",
"productName": "YourApp",
"asar": true,
"asarUnpack": [
"**/*.node"
]
}
}asar 的作用与局限
| 特性 | 说明 |
|---|---|
| ✅ 优点 | 将源码打包为单个归档文件,防止直接浏览文件结构 |
| ❌ 局限 | asar 可以被轻松解包(npx asar extract app.asar output/) |
| 💡 结论 | 必须配合加密使用,单独使用几乎无效 |
3.2 asar 二次加密(关键步骤)
加密脚本
js
// scripts/encrypt-asar.js
const fs = require('fs');
const crypto = require('crypto');
const path = require('path');
const ASAR_FILE = path.join(__dirname, '../dist/mac/YourApp.app/Contents/Resources/app.asar');
const ENCRYPTED_FILE = ASAR_FILE + '.enc';
const SECRET_KEY = process.env.ASAR_ENCRYPTION_KEY || 'your-secret-key-here';
function encryptAsar() {
if (!fs.existsSync(ASAR_FILE)) {
console.error('❌ asar 文件不存在,请先执行构建');
process.exit(1);
}
console.log('🔐 开始加密 asar...');
// 读取原始 asar
const originalData = fs.readFileSync(ASAR_FILE);
// 生成随机 IV
const iv = crypto.randomBytes(16);
// 创建加密器
const cipher = crypto.createCipheriv('aes-256-cbc',
Buffer.from(SECRET_KEY.padEnd(32, '0').slice(0, 32)),
iv
);
// 加密
const encrypted = Buffer.concat([
iv, // 前 16 字节存储 IV
cipher.update(originalData),
cipher.final()
]);
// 写入加密后的文件
fs.writeFileSync(ENCRYPTED_FILE, encrypted);
// 删除原始 asar(重要!)
fs.unlinkSync(ASAR_FILE);
// 计算 Hash 用于完整性校验
const hash = crypto.createHash('sha256').update(encrypted).digest('hex');
fs.writeFileSync(ENCRYPTED_FILE + '.sha256', hash);
console.log('✅ asar 加密完成');
console.log(`📁 加密文件: ${ENCRYPTED_FILE}`);
console.log(`🔑 SHA256: ${hash}`);
}
encryptAsar();集成到构建流程
json
// package.json
{
"scripts": {
"build": "vite build && electron-builder",
"postbuild": "node scripts/encrypt-asar.js"
}
}⚙️ 四、运行时解密与加载(核心技术)
4.1 主进程启动解密
js
// main/secure-loader.js
const fs = require('fs');
const crypto = require('crypto');
const path = require('path');
const { app } = require('electron');
class SecureLoader {
constructor() {
this.resourcesPath = app.isPackaged
? path.join(process.resourcesPath)
: path.join(__dirname, '../dist');
this.encryptionKey = process.env.ASAR_ENCRYPTION_KEY || 'your-secret-key-here';
}
// 解密 asar 到内存(不落盘)
loadEncryptedAsar() {
const encryptedPath = path.join(this.resourcesPath, 'app.asar.enc');
const hashPath = encryptedPath + '.sha256';
// 1. 检查文件存在性
if (!fs.existsSync(encryptedPath)) {
console.error('❌ 加密的 asar 文件不存在');
app.quit();
return null;
}
// 2. 完整性校验
if (fs.existsSync(hashPath)) {
const expectedHash = fs.readFileSync(hashPath, 'utf-8');
const actualHash = crypto.createHash('sha256')
.update(fs.readFileSync(encryptedPath))
.digest('hex');
if (expectedHash !== actualHash) {
console.error('❌ asar 文件已被篡改');
app.quit();
return null;
}
}
// 3. 解密
try {
const encryptedData = fs.readFileSync(encryptedPath);
const iv = encryptedData.slice(0, 16);
const encrypted = encryptedData.slice(16);
const decipher = crypto.createDecipheriv('aes-256-cbc',
Buffer.from(this.encryptionKey.padEnd(32, '0').slice(0, 32)),
iv
);
const decrypted = Buffer.concat([
decipher.update(encrypted),
decipher.final()
]);
console.log('✅ asar 解密成功');
return decrypted;
} catch (error) {
console.error('❌ 解密失败:', error.message);
app.quit();
return null;
}
}
// 从内存加载模块(高级用法)
loadModuleFromMemory(moduleName, decryptedBuffer) {
const vm = require('vm');
const Module = require('module');
// 创建沙箱上下文
const sandbox = {
module: { exports: {} },
exports: {},
require: require,
__filename: moduleName,
__dirname: path.dirname(moduleName)
};
// 执行代码
const code = decryptedBuffer.toString('utf-8');
vm.runInNewContext(code, sandbox);
return sandbox.module.exports;
}
}
module.exports = new SecureLoader();4.2 在主进程中调用
js
// main/main.js
const { app, BrowserWindow } = require('electron');
const SecureLoader = require('./secure-loader');
let mainWindow;
function createWindow() {
// 先解密 asar
const decryptedAsar = SecureLoader.loadEncryptedAsar();
if (!decryptedAsar) {
return; // 解密失败已退出
}
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
});
// 加载页面
mainWindow.loadURL('app://./index.html');
}
app.whenReady().then(createWindow);🛡️ 五、运行时安全防护
5.1 禁用开发者工具
js
// main/security.js
function disableDevTools(window) {
// 禁止打开 DevTools
window.webContents.on('devtools-opened', () => {
console.warn('⚠️ 检测到 DevTools 被打开,立即关闭');
window.webContents.closeDevTools();
});
// 禁用快捷键
window.webContents.on('before-input-event', (event, input) => {
if (
(input.control || input.meta) &&
input.key.toLowerCase() === 'i'
) {
event.preventDefault();
}
if (
(input.control || input.meta) &&
input.key.toLowerCase() === 'u'
) {
event.preventDefault();
}
});
}5.2 防调试检测
js
// main/anti-debug.js
class AntiDebug {
constructor() {
this.checkInterval = null;
this.suspiciousCount = 0;
}
start() {
// 方法 1: debugger 语句时间差检测
this.checkInterval = setInterval(() => {
const startTime = performance.now();
debugger; // 如果被调试,这里会暂停
const endTime = performance.now();
// 如果暂停超过 100ms,认为正在被调试
if (endTime - startTime > 100) {
this.suspiciousCount++;
console.warn(`⚠️ 检测到调试行为 (${this.suspiciousCount}/3)`);
if (this.suspiciousCount >= 3) {
console.error('🚫 多次检测到调试,应用将退出');
setTimeout(() => process.exit(1), 1000);
}
} else {
this.suspiciousCount = Math.max(0, this.suspiciousCount - 1);
}
}, 2000);
// 方法 2: 检测常见调试工具
this.detectDebuggerTools();
}
detectDebuggerTools() {
// 检测 Chrome DevTools Protocol
const net = require('net');
const server = net.createServer();
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error('🚫 检测到端口占用,可能正在被调试');
process.exit(1);
}
});
server.listen(9229, () => {
server.close();
});
}
stop() {
if (this.checkInterval) {
clearInterval(this.checkInterval);
}
}
}
module.exports = new AntiDebug();5.3 完整性校验体系
js
// main/integrity-check.js
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { app } = require('electron');
class IntegrityChecker {
constructor() {
this.expectedHashes = {
'app.asar.enc': '预设的SHA256值',
'main.js': '预设的SHA256值',
'preload.js': '预设的SHA256值'
};
}
// 计算文件 Hash
calculateHash(filePath) {
const data = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(data).digest('hex');
}
// 校验所有关键文件
verifyAll() {
const resourcesPath = app.isPackaged
? process.resourcesPath
: path.join(__dirname, '../dist');
for (const [file, expectedHash] of Object.entries(this.expectedHashes)) {
const filePath = path.join(resourcesPath, file);
if (!fs.existsSync(filePath)) {
console.error(`❌ 关键文件缺失: ${file}`);
return false;
}
const actualHash = this.calculateHash(filePath);
if (actualHash !== expectedHash) {
console.error(`❌ 文件被篡改: ${file}`);
console.error(` 期望: ${expectedHash}`);
console.error(` 实际: ${actualHash}`);
return false;
}
}
console.log('✅ 完整性校验通过');
return true;
}
// 定期校验(防止运行时替换)
startPeriodicCheck(intervalMs = 5 * 60 * 1000) {
setInterval(() => {
if (!this.verifyAll()) {
console.error('🚫 周期性校验失败,应用将退出');
setTimeout(() => app.quit(), 1000);
}
}, intervalMs);
}
}
module.exports = new IntegrityChecker();📡 六、分发层安全
6.1 代码签名(必须)
Windows 代码签名
json
// package.json
{
"build": {
"win": {
"sign": "./scripts/sign-windows.js",
"certificateFile": "cert.pfx",
"certificatePassword": "your-password"
}
}
}macOS 代码签名
json
{
"build": {
"mac": {
"identity": "Developer ID Application: Your Company (XXXXX)",
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.inherit.plist"
}
}
}xml
<!-- build/entitlements.mac.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>6.2 安全自动更新
electron-updater 配置
js
// main/updater.js
const { autoUpdater } = require('electron-updater');
const { dialog } = require('electron');
class SecureUpdater {
constructor() {
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true;
// 设置更新服务器(必须 HTTPS)
autoUpdater.setFeedURL({
provider: 'generic',
url: 'https://updates.yourapp.com',
channel: 'latest'
});
}
async checkForUpdates() {
try {
const info = await autoUpdater.checkForUpdates();
if (info.updateInfo) {
console.log(`发现新版本: ${info.updateInfo.version}`);
// 校验更新包 Hash
const expectedSha512 = info.updateInfo.files[0].sha512;
console.log(`预期 Hash: ${expectedSha512}`);
// 下载更新
autoUpdater.downloadUpdate();
}
} catch (error) {
console.error('更新检查失败:', error);
}
}
init() {
autoUpdater.on('update-downloaded', (info) => {
dialog.showMessageBox({
type: 'info',
title: '更新就绪',
message: `新版本 ${info.version} 已下载完成`,
detail: '应用将在重启后自动安装',
buttons: ['立即重启', '稍后']
}).then(({ response }) => {
if (response === 0) {
autoUpdater.quitAndInstall();
}
});
});
autoUpdater.on('error', (error) => {
console.error('更新失败:', error);
});
}
}
module.exports = new SecureUpdater();🏢 七、企业级终极方案
7.1 前后端分离架构
mermaid
graph TB
subgraph 客户端
A[Electron Shell] --> B[混淆的前端 UI]
B --> C[Preload 脚本]
end
subgraph 服务端
D[API Gateway] --> E[认证服务]
D --> F[业务逻辑服务]
D --> G[数据存储服务]
E --> H[Redis 会话管理]
F --> I[MySQL/PostgreSQL]
end
C -->|HTTPS + JWT| D
D -->|加密响应| C
style A fill:#e1f5ff
style B fill:#fff4e1
style D fill:#e8f5e97.2 方案对比
| 方案 | 安全性 | 实现难度 | 维护成本 | 适用场景 |
|---|---|---|---|---|
| 纯客户端 | ⭐⭐ | 低 | 低 | 个人项目、演示版 |
| 混淆 + 加密 | ⭐⭐⭐ | 中 | 中 | 小型商业软件 |
| 完整加固 | ⭐⭐⭐⭐ | 高 | 高 | 中型商业软件 |
| 前后端分离 | ⭐⭐⭐⭐⭐ | 很高 | 很高 | 大型企业应用 |
💣 八、常见错误与避坑指南
8.1 典型错误清单
| 错误做法 | 风险等级 | 后果 | 正确做法 |
|---|---|---|---|
| ❌ 只用 asar 打包 | 🔴 极高 | 可一键解包 | asar + 二次加密 |
| ❌ 只混淆不加密 | 🟠 高 | 工具可还原 | 混淆 + 字符串加密 |
| ❌ 密钥硬编码 | 🔴 极高 | 等同于明文 | 环境变量 + 动态获取 |
| ❌ 解密后落盘 | 🟠 高 | 可被内存 dump | 内存中解密执行 |
| ❌ 忽略完整性校验 | 🟡 中 | 文件可被替换 | SHA256 定期校验 |
| ❌ 不禁用 DevTools | 🟡 中 | 方便调试分析 | 禁用 + 防调试检测 |
8.2 安全检查清单
markdown
## 发布前安全检查
- [ ] JS 代码已混淆(javascript-obfuscator)
- [ ] 敏感字符串已加密(AES-256)
- [ ] asar 已二次加密
- [ ] 加密密钥未硬编码
- [ ] 运行时解密不落盘
- [ ] DevTools 已禁用
- [ ] 防调试机制已启用
- [ ] 文件完整性校验已配置
- [ ] 代码签名已完成
- [ ] 更新服务器使用 HTTPS
- [ ] 核心逻辑已移至后端(如适用)🚀 九、推荐组合方案(生产可用)
9.1 标准加固方案
✅ javascript-obfuscator → 代码混淆
✅ AES-256-CBC → 字符串加密
✅ asar 打包 → 基础归档
✅ asar 二次加密 → 增强保护
✅ 内存解密加载 → 避免落盘
✅ SHA256 完整性校验 → 防篡改
✅ 防调试 + 禁用 DevTools → 增加逆向难度
✅ 代码签名 → 防分发篡改
✅ 核心逻辑后移至 API → 终极保护9.2 实施优先级
| 优先级 | 措施 | 投入产出比 |
|---|---|---|
| P0 | asar + 混淆 | ⭐⭐⭐⭐⭐ |
| P0 | 核心逻辑后端化 | ⭐⭐⭐⭐⭐ |
| P1 | asar 二次加密 | ⭐⭐⭐⭐ |
| P1 | 字符串加密 | ⭐⭐⭐⭐ |
| P2 | 防调试检测 | ⭐⭐⭐ |
| P2 | 代码签名 | ⭐⭐⭐ |
| P3 | 完整性定期校验 | ⭐⭐ |
📝 十、面试标准回答模板
Q1: Electron 应用如何防止源码泄露?
回答要点:
"Electron 由于基于 Chromium 和 Node.js,源码天然是明文的。我们采用多层防护策略:
- 源码层:使用
javascript-obfuscator进行代码混淆,包括控制流扁平化、字符串数组化、死代码注入等;- 构建层:除了默认的 asar 打包外,还对 asar 进行 AES-256 二次加密;
- 运行时:在内存中解密 asar,避免落盘;同时禁用 DevTools 并加入防调试检测;
- 架构层:最核心的业务逻辑放在后端 API,客户端只做 UI 展示。
这样即使客户端被破解,也无法获取核心算法和数据。"
Q2: asar 打包是否安全?
回答要点:
"单独使用 asar 非常不安全。因为:
- asar 只是简单的归档格式,可以用
npx asar extract一键解包- 没有任何加密或混淆
正确做法是:
- asar 打包后进行 AES 加密
- 运行时在内存中解密
- 配合代码混淆使用
这样才能真正提高破解成本。"
Q3: 如何保护 API 密钥等敏感信息?
回答要点:
"有三种方案,按安全性递增:
- 编译时加密:在构建阶段用 AES 加密密钥,运行时解密(但仍可在内存中找到)
- 环境变量:通过 CI/CD 注入,不写入代码库
- 后端代理(推荐):客户端不存储任何密钥,所有敏感请求通过自己的后端转发
对于商业产品,强烈推荐第三种方案,从根本上杜绝客户端泄露风险。"
🧠 十一、记忆口诀
Electron 安全要记牢,四层防护不能少:
混淆加密第一步,asar 打包加密封。
内存解密不落盘,防调校验双保险。
代码签名防篡改,核心逻辑放后端。
绝对安全做不到,提高成本是王道!📚 十二、相关资源
| 资源类型 | 链接 | 说明 |
|---|---|---|
| 官方文档 | electronjs.org/docs/tutorial/security | Electron 安全最佳实践 |
| 混淆工具 | javascript-obfuscator | GitHub 12k+ Stars |
| 代码签名 | Microsoft SignTool | Windows 代码签名工具 |
| 更新框架 | electron-updater | 自动更新解决方案 |
| 安全审计 | Electron Security Checklist | 官方安全检查清单 |